 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal;

 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.Iterator ;
 import java.util.Map ;

 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.MultiStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.dnd.DND;
 import org.eclipse.swt.dnd.DropTarget;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.ui.IMemento;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.StartupThreading.StartupRunnable;
 import org.eclipse.ui.internal.presentations.PresentationSerializer;
 import org.eclipse.ui.presentations.IStackPresentationSite;
 import org.eclipse.ui.presentations.StackPresentation;

 /**
  * Represents the area set aside for editor workbooks.
  * This container only accepts EditorStack and PartSash
  * as layout parts.
  *
  * Note no views are allowed within this container.
  */
 public class EditorSashContainer extends PartSashContainer {

     static final String DEFAULT_WORKBOOK_ID = "DefaultEditorWorkbook";//$NON-NLS-1$

     private ArrayList editorWorkbooks = new ArrayList (3);

     private EditorStack activeEditorWorkbook;

     private DropTarget dropTarget;

     public EditorSashContainer(String editorId, WorkbenchPage page, Composite parent) {
         super(editorId, page, parent);

         createDefaultWorkbook();
     }

     
     /**
      * Add an editor to the active workbook.
      */
     public void addEditor(EditorPane pane, EditorStack stack) {
         //EditorStack workbook = getActiveWorkbook();
 stack.add(pane);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.PartSashContainer#addChild(org.eclipse.ui.internal.PartSashContainer.RelationshipInfo)
      */
     protected void addChild(RelationshipInfo info) {
         super.addChild(info);
         
         updateStackButtons();
     }
     
     /**
      * Hides the min/max buttons for all editor stacks
      * -except- for the upper/left one.
      */
     public void updateStackButtons() {
          // This is applicable only when the new
 // min/max behaviour is being used
 Perspective persp = getPage().getActivePerspective();
         if (!Perspective.useNewMinMax(persp))
             return;
         
         // Find the upper Right editor stack
 LayoutPart[] stacks = getChildren();
         EditorStack winner = getUpperRightEditorStack(stacks);
         
         // Now hide the buttons for all but the upper right stack
 for (int i = 0; i < stacks.length; i++) {
             if (!(stacks[i] instanceof EditorStack))
                 continue;
             ((EditorStack)stacks[i]).showMinMax(stacks[i] == winner);
         }
         
         // Force the stack's presentation state to match its perspective
 persp.refreshEditorAreaVisibility();
     }

     /**
      * @param stacks
      * @return the EditorStack in the upper right position
      */
     public EditorStack getUpperRightEditorStack(LayoutPart[] stacks) {
         if (stacks == null)
             stacks = getChildren();
         
         // Find the upper Right editor stack
 EditorStack winner = null;
         Rectangle winnerRect = null;

         for (int i = 0; i < stacks.length; i++) {
             if (!(stacks[i] instanceof EditorStack))
                 continue;
             
             EditorStack stack = (EditorStack) stacks[i];
             Rectangle bb = stack.getBounds();
             if (winnerRect == null ||
                 bb.y < winnerRect.y ||
                 (bb.y == winnerRect.y && bb.x > winnerRect.x)) {
                 winner = stack;
                 winnerRect = bb;
             }
         }
         
         return winner;
     }

     /**
      * Notification that a child layout part has been
      * added to the container. Subclasses may override
      * this method to perform any container specific
      * work.
      */
     protected void childAdded(LayoutPart child) {
         super.childAdded(child);
         
         if (child instanceof EditorStack) {
             editorWorkbooks.add(child);
         }
     }

     /**
      * Notification that a child layout part has been
      * removed from the container. Subclasses may override
      * this method to perform any container specific
      * work.
      */
     protected void childRemoved(LayoutPart child) {
         super.childRemoved(child);
         
         if (child instanceof EditorStack) {
             editorWorkbooks.remove(child);
             if (activeEditorWorkbook == child) {
                 setActiveWorkbook(null, false);
             }
             
             updateStackButtons();
         }
     }

     protected EditorStack createDefaultWorkbook() {
         EditorStack newWorkbook = EditorStack.newEditorWorkbook(this, page);
         newWorkbook.setID(DEFAULT_WORKBOOK_ID);
         add(newWorkbook);
         return newWorkbook;
     }

     /**
      * Subclasses override this method to specify
      * the composite to use to parent all children
      * layout parts it contains.
      */
     protected Composite createParent(Composite parentWidget) {
         return new Composite(parentWidget, SWT.NONE);
     }

     /**
      * Dispose of the editor area.
      */
     public void dispose() {
         // Free editor workbooks.
 editorWorkbooks.clear();

         // Free rest.
 super.dispose();
     }

     /**
      * Subclasses override this method to dispose
      * of any swt resources created during createParent.
      */
     protected void disposeParent() {
         this.parent.dispose();
     }

     /**
      * Return the editor workbook which is active.
      */
     public EditorStack getActiveWorkbook() {
         if (activeEditorWorkbook == null) {
             if (editorWorkbooks.size() < 1) {
                 setActiveWorkbook(createDefaultWorkbook(), false);
             } else {
                 setActiveWorkbook((EditorStack) editorWorkbooks.get(0), false);
             }
         }

         return activeEditorWorkbook;
     }

     /**
      * Return the editor workbook id which is active.
      */
     public String getActiveWorkbookID() {
         return getActiveWorkbook().getID();
     }

     /**
      * Return the all the editor workbooks.
      */
     public ArrayList getEditorWorkbooks() {
         return (ArrayList ) editorWorkbooks.clone();
     }

     /**
      * Return the all the editor workbooks.
      */
     public int getEditorWorkbookCount() {
         return editorWorkbooks.size();
     }

     /**
      * Return true is the workbook specified
      * is the active one.
      */
     protected boolean isActiveWorkbook(EditorStack workbook) {
         return activeEditorWorkbook == workbook;
     }

     /**
      * Find the sashs around the specified part.
      */
     public void findSashes(LayoutPart pane, PartPane.Sashes sashes) {
         //Find the sashes around the current editor and
 //then the sashes around the editor area.
 super.findSashes(pane, sashes);

         ILayoutContainer container = getContainer();
         if (container != null) {
             container.findSashes(this, sashes);
         }
     }

     /**
      * Remove all the editors
      */
     public void removeAllEditors() {
         EditorStack currentWorkbook = getActiveWorkbook();

         // Iterate over a copy so the original can be modified.
 Iterator workbooks = ((ArrayList ) editorWorkbooks.clone()).iterator();
         while (workbooks.hasNext()) {
             EditorStack workbook = (EditorStack) workbooks.next();
             workbook.removeAll();
             if (workbook != currentWorkbook) {
                 remove(workbook);
                 workbook.dispose();
             }
         }
     }

     /**
      * Remove an editor from its' workbook.
      */
     public void removeEditor(EditorPane pane) {
         EditorStack workbook = pane.getWorkbook();
         if (workbook == null) {
             return;
         }
         workbook.remove(pane);

         // remove the editor workbook if empty
 if (workbook.getItemCount() < 1 /* && editorWorkbooks.size() > 1*/) {
             // If the user closes the last editor and the editor area
 // is maximized, restore it
 Perspective persp = getPage().getActivePerspective();
             if (Perspective.useNewMinMax(persp)) {
                 if (persp.getPresentation().getMaximizedStack() instanceof EditorStack)
                     persp.getPresentation().getMaximizedStack().
                         setState(IStackPresentationSite.STATE_RESTORED);
             }

             remove(workbook);
             workbook.dispose();
         }
     }

     /**
      * @see IPersistablePart
      */
     public IStatus restoreState(IMemento memento) {
         MultiStatus result = new MultiStatus(
                 PlatformUI.PLUGIN_ID,
                 IStatus.OK,
                 WorkbenchMessages.RootLayoutContainer_problemsRestoringPerspective, null);

         // Remove the default editor workbook that is
 // initialy created with the editor area.
 if (children != null) {
             StartupThreading.runWithoutExceptions(new StartupRunnable() {

                 public void runWithException() throws Throwable {
                     EditorStack defaultWorkbook = null;
                     for (int i = 0; i < children.size(); i++) {
                         LayoutPart child = (LayoutPart) children.get(i);
                         if (child.getID() == DEFAULT_WORKBOOK_ID) {
                             defaultWorkbook = (EditorStack) child;
                             if (defaultWorkbook.getItemCount() > 0) {
                                 defaultWorkbook = null;
                             }
                         }
                     }
                     if (defaultWorkbook != null) {
                         remove(defaultWorkbook);
                     }
                 }});
             
         }

         // Restore the relationship/layout
 IMemento[] infos = memento.getChildren(IWorkbenchConstants.TAG_INFO);
         final Map mapIDtoPart = new HashMap (infos.length);

         for (int i = 0; i < infos.length; i++) {
             // Get the info details.
 IMemento childMem = infos[i];
             final String partID = childMem.getString(IWorkbenchConstants.TAG_PART);
             final String relativeID = childMem
                     .getString(IWorkbenchConstants.TAG_RELATIVE);
             int relationship = 0;
             int left = 0, right = 0;
             float ratio = 0.5f;
             if (relativeID != null) {
                 relationship = childMem.getInteger(
                         IWorkbenchConstants.TAG_RELATIONSHIP).intValue();
                 Float ratioFloat = childMem
                         .getFloat(IWorkbenchConstants.TAG_RATIO);
                 Integer leftInt = childMem
                         .getInteger(IWorkbenchConstants.TAG_RATIO_LEFT);
                 Integer rightInt = childMem
                         .getInteger(IWorkbenchConstants.TAG_RATIO_RIGHT);
                 if (leftInt != null && rightInt != null) {
                     left = leftInt.intValue();
                     right = rightInt.intValue();
                 } else if (ratioFloat != null) {
                     ratio = ratioFloat.floatValue();
                 }
             }

             final EditorStack workbook [] = new EditorStack[1];
             StartupThreading.runWithoutExceptions(new StartupRunnable() {

                 public void runWithException() throws Throwable {
                     // Create the part.
 workbook[0] = EditorStack.newEditorWorkbook(EditorSashContainer.this, page);
                     workbook[0].setID(partID);
                     // 1FUN70C: ITPUI:WIN - Shouldn't set Container when not active
 workbook[0].setContainer(EditorSashContainer.this);
                 }});
             

             IMemento workbookMemento = childMem
                     .getChild(IWorkbenchConstants.TAG_FOLDER);
             if (workbookMemento != null) {
                 result.add(workbook[0].restoreState(workbookMemento));
             }

             final int myLeft = left, myRight = right, myRelationship = relationship;
             final float myRatio = ratio;
             StartupThreading.runWithoutExceptions(new StartupRunnable() {

                 public void runWithException() throws Throwable {
                     // Add the part to the layout
 if (relativeID == null) {
                         add(workbook[0]);
                     } else {
                         LayoutPart refPart = (LayoutPart) mapIDtoPart.get(relativeID);
                         if (refPart != null) {
                             //$TODO pass in left and right
 if (myLeft == 0 || myRight == 0) {
                                 add(workbook[0], myRelationship, myRatio, refPart);
                             } else {
                                 add(workbook[0], myRelationship, myLeft, myRight, refPart);
                             }
                         } else {
                             WorkbenchPlugin
                                     .log("Unable to find part for ID: " + relativeID);//$NON-NLS-1$
 }
                     }
                 }});
             
             mapIDtoPart.put(partID, workbook[0]);
         }

         return result;
     }

     /**
      * @see IPersistablePart
      */
     public IStatus saveState(IMemento memento) {
         RelationshipInfo[] relationships = computeRelation();
         MultiStatus result = new MultiStatus(
                 PlatformUI.PLUGIN_ID,
                 IStatus.OK,
                 WorkbenchMessages.RootLayoutContainer_problemsSavingPerspective, null);

         for (int i = 0; i < relationships.length; i++) {
             // Save the relationship info ..
 // private LayoutPart part;
 // private int relationship;
 // private float ratio;
 // private LayoutPart relative;
 RelationshipInfo info = relationships[i];
             IMemento childMem = memento
                     .createChild(IWorkbenchConstants.TAG_INFO);
             childMem.putString(IWorkbenchConstants.TAG_PART, info.part.getID());

             EditorStack stack = (EditorStack) info.part;
             if (stack != null) {
                 IMemento folderMem = childMem
                         .createChild(IWorkbenchConstants.TAG_FOLDER);
                 result.add(stack.saveState(folderMem));
             }

             if (info.relative != null) {
                 childMem.putString(IWorkbenchConstants.TAG_RELATIVE,
                         info.relative.getID());
                 childMem.putInteger(IWorkbenchConstants.TAG_RELATIONSHIP,
                         info.relationship);
                 childMem.putInteger(IWorkbenchConstants.TAG_RATIO_LEFT,
                         info.left);
                 childMem.putInteger(IWorkbenchConstants.TAG_RATIO_RIGHT,
                         info.right);
                 // Note: "ratio" is not used in newer versions of Eclipse, which use "left"
 // and "right" (above) instead
 childMem.putFloat(IWorkbenchConstants.TAG_RATIO, info
                         .getRatio());
             }
         }
         
         return result;
     }

     /**
      * Set the editor workbook which is active.
      */
     public void setActiveWorkbook(EditorStack newWorkbook, boolean hasFocus) {
         if (newWorkbook != null) {
             if (newWorkbook.isDisposed()) {
                 return;
             }
             if (!editorWorkbooks.contains(newWorkbook)) {
                 return;
             }
         }
         EditorStack oldWorkbook = activeEditorWorkbook;
         activeEditorWorkbook = newWorkbook;

         if (oldWorkbook != null && oldWorkbook != newWorkbook) {
             oldWorkbook.setActive(StackPresentation.AS_INACTIVE);
         }

         if (newWorkbook != null) {
             if (hasFocus) {
                 newWorkbook.setActive(StackPresentation.AS_ACTIVE_FOCUS);
             } else {
                 newWorkbook.setActive(StackPresentation.AS_ACTIVE_NOFOCUS);
             }
         }

         updateTabList();
     }

     /**
      * Set the editor workbook which is active.
      */
     public void setActiveWorkbookFromID(String id) {
         for (int i = 0; i < editorWorkbooks.size(); i++) {
             EditorStack workbook = (EditorStack) editorWorkbooks.get(i);
             if (workbook.getID().equals(id)) {
                 setActiveWorkbook(workbook, false);
             }
         }
     }
     
     public EditorStack getWorkbookFromID(String id) {
         for (int i = 0; i < editorWorkbooks.size(); i++) {
             EditorStack workbook = (EditorStack) editorWorkbooks.get(i);
             if (workbook.getID().equals(id)) {
                 return workbook;
             }
         }
         
         return null;
     }

     /**
      * Updates the editor area's tab list to include the active
      * editor and its tab.
      */
     public void updateTabList() {
         Composite parent = getParent();
         if (parent != null) { // parent may be null on startup
 EditorStack wb = getActiveWorkbook();
             if (wb == null) {
                 parent.setTabList(new Control[0]);
             } else {
                 parent.setTabList(wb.getTabList());
             }
         }
     }

     /**
      * @see org.eclipse.ui.internal.LayoutPart#createControl(org.eclipse.swt.widgets.Composite)
      */
     public void createControl(Composite parent) {
         super.createControl(parent);

         //let the user drop files/editor input on the editor area
 addDropSupport();
     }

     private void addDropSupport() {
         if (dropTarget == null) {
             WorkbenchWindowConfigurer winConfigurer = ((WorkbenchWindow) page
                     .getWorkbenchWindow()).getWindowConfigurer();

             dropTarget = new DropTarget(getControl(), DND.DROP_DEFAULT
                     | DND.DROP_COPY | DND.DROP_LINK);
             dropTarget.setTransfer(winConfigurer.getTransfers());
             if (winConfigurer.getDropTargetListener() != null) {
                 dropTarget.addDropListener(winConfigurer
                         .getDropTargetListener());
             }
         }
     }

     /* package */DropTarget getDropTarget() {
         return dropTarget;
     }

     /**
      * @see org.eclipse.ui.internal.LayoutPart#getImportance()
      */
     public boolean isCompressible() {
         //Added for bug 19524
 return true;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.PartSashContainer#isStackType(org.eclipse.ui.internal.LayoutPart)
      */
     public boolean isStackType(LayoutPart toTest) {
         return (toTest instanceof EditorStack);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.PartSashContainer#isPaneType(org.eclipse.ui.internal.LayoutPart)
      */
     public boolean isPaneType(LayoutPart toTest) {
         return (toTest instanceof EditorPane);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.PartSashContainer#createStack(org.eclipse.ui.internal.LayoutPart)
      */
     protected PartStack createStack() {
         EditorStack newWorkbook = EditorStack.newEditorWorkbook(this, page);

         return newWorkbook;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.PartSashContainer#setVisiblePart(org.eclipse.ui.internal.ILayoutContainer, org.eclipse.ui.internal.LayoutPart)
      */
     protected void setVisiblePart(ILayoutContainer container,
             LayoutPart visiblePart) {
         EditorStack refPart = (EditorStack) container;

         refPart.becomeActiveWorkbook(true);
         refPart.setSelection(visiblePart);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.PartSashContainer#getVisiblePart(org.eclipse.ui.internal.ILayoutContainer)
      */
     protected LayoutPart getVisiblePart(ILayoutContainer container) {
         EditorStack refPart = (EditorStack) container;

         return refPart.getSelection();
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.PartSashContainer#pickPartToZoom()
      */
     public LayoutPart pickPartToZoom() {
         return getActiveWorkbook();
     }
     
     /**
      * Restore the presentation state. Loop over the workbooks, create the appropriate serializer and pass to the presentation.
      *
      * @param areaMem the memento containing presentation
      * @return the restoration status
      */
     public IStatus restorePresentationState(IMemento areaMem) {
         for (Iterator i = getEditorWorkbooks().iterator(); i.hasNext();) {
             final EditorStack workbook = (EditorStack) i.next();
             final IMemento memento = workbook.getSavedPresentationState();
             if (memento == null) {
                 continue;
             }
             final PresentationSerializer serializer = new PresentationSerializer(
                     workbook.getPresentableParts());
             StartupThreading.runWithoutExceptions(new StartupRunnable(){

                 public void runWithException() throws Throwable {
                      workbook.getPresentation().restoreState(serializer, memento);
                 }});
            
         }
         return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, 0, "", null); //$NON-NLS-1$
 }
 }

